package l3s.wikirevisions;

import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.Resolution;

import org.apache.lucene.search.similarities.Similarity.ExactSimScorer;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest.METHOD;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.util.NamedList;

/**
 * @author pham@l3s.de
 */
public class WikiActionBean implements ActionBean {
	private static final int NUM_ROWS = 20;
	
	private int pageEvent = 1;

    private String query = "*:*";
    private Date fromDate;
    private Date toDate;
        
    private String entities;
    private List<Event> events;
    private int numFound;
    private int numFoundEvents;
    
    private List<Filter> filters = new ArrayList<>();
    private String addedFilter = "";
    private String appliedFilters = "";
    
    
    private String fullQuery;
        
	private static SolrServer solrServer;

    private ActionBeanContext context;
    public ActionBeanContext getContext() { return context; }
    public void setContext(ActionBeanContext context) { this.context = context; }
    
    private int currentEventIndex;
    
    public String getQuery(){
    	return query;
    }            
    
    public List<Filter> getFilters() {
		return filters;
	}
	public void setFilters(List<Filter> filters) {
		this.filters = filters;
	}
	public String getAddedFilter() {
		return addedFilter;
	}
	public void setAddedFilter(String addedFilter) {
		this.addedFilter = addedFilter;
		addedFilter = normalizeFilter(addedFilter);
		this.appliedFilters = addedFilter;
	}
	
	public String normalizeFilter(String filters){
		String r = "";
		String pre = "";
		for (String str: filters.split("###"))
		{
			if (str.length() > 0 && !str.equals(pre))
				r += "###" + str;
			pre = str;
		}
		return r;		
	}
	public String getAppliedFilters() {
		return appliedFilters;
	}
	
	public List<String> getAppliedFiltersList(){
		List<String> r = new ArrayList<>();
		for (String str: appliedFilters.split("###"))
			if (str.length() > 0)
				r.add(str);
		return r;
	}
	public void setAppliedFilters(String appliedFilters) {
		this.appliedFilters = appliedFilters;
	}
	public String getFullQuery(){
    	//return fullQuery;
		return URLDecoder.decode(fullQuery);
    }        
    
	public int getPageEvent() {
		return pageEvent;
	}
    
    public int getPageEventMin(){
    	return Math.max(1, pageEvent - 5);
    }
    
    public int getPageEventMax(){
    	return Math.min(getPageEventMin() + 10, getEventPagesNr());
    }
            
	public String getEntities() {
		return entities;
	}
	public void setEntities(String entities) {
		this.entities = entities;
	}
	public Date getFromDate() {
		return fromDate;
	}
	
	public String getFromDateStr(){
		if (fromDate != null)
			return (new SimpleDateFormat("yyyy-MM-dd")).format(fromDate);
		return "";
	}
	
	public String getFromDateStrToDisplay(){
		String res = "";
		if (fromDate != null)
			res = new SimpleDateFormat("dd/MM/yyyy").format(fromDate);
	
		return res;
	}
	
	public String getDateStrWithYearOnly(Date d){
		if (d != null)
			//return (new SimpleDateFormat("yyyy-MM-dd")).format(d);
			return (new SimpleDateFormat("yyyy")).format(d);
		return "";
		
	}
	
	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}
	public Date getToDate() {
		return toDate;
	}
	
	public String getToDateStr(){
		if (toDate != null)
			return (new SimpleDateFormat("yyyy-MM-dd")).format(toDate);
		return "";
	}
	
	public String getToDateStrToDisplay(){
		String res = "";
		if (toDate != null)
			res = new SimpleDateFormat("dd/MM/yyyy").format(toDate);
		
		return res;
	}
	
	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}
	public void setPageEvent(int page) {
		this.pageEvent = page;
	}
	
	public int getNumRows(){
		return NUM_ROWS;
	}
		
	public void setQuery(String query){
    	this.query = query;
    }

    public List<Event> getEvents(){
    	System.out.println("Get events: " + events.size());
    	return events;
    }
        
	public int getNumFound() {
		return numFound;
	}
	
	public int getNumFoundEvents(){
		return numFoundEvents;
	}
	
	public void setNumFound(int numFound) {
		this.numFound = numFound;
	}		
	
	public Map<String, String> getEntitiesList(){
		return Utils.getEntities();
	}
	
	public int getCurrentEventIndex(){
		return currentEventIndex;
	}
	public static void modifyResponse(QueryResponse response) {
		if(response==null)
			return;
		NamedList<Object> list=response.getResponse();
		Object groupedObj=list.get("grouped");
		if(groupedObj==null)
			return;
		SolrDocumentList allDocs=new SolrDocumentList();
		NamedList<Object> grouped=(NamedList<Object>)groupedObj;
		grouped=(NamedList<Object>)grouped.get("page_ID");



		//set the number of results to be returned to the interface
		//duplicates counted as being different results
		//allDocs.setMaxScore(((Integer)grouped.get("matches")).floatValue());//XXX: use max score as temp value
		//duplicates are counted just once
		Integer numFound=(Integer)grouped.get("ngroups");
		System.out.println("numFound: "+numFound);
		allDocs.setNumFound(numFound);
		
		
		ArrayList<NamedList<Object>> groups=(ArrayList<NamedList<Object>>)grouped.get("groups");
		System.out.println("groups: "+groups.size());
		for(NamedList<Object> group:groups) {
			//System.out.println("group: "+group);
			SolrDocumentList docList=(SolrDocumentList)group.get("doclist");
			//System.out.println("docList: "+docList);

			allDocs.addAll(docList);

		}
		list.remove("grouped");
		list.add("response", allDocs);
		System.out.println("All docs: " + allDocs.size());
		response.setResponse(list);
	}		
	
	public int getIndexOf(Event event, List<Event> events){
		int idx = 1;
		for (Event e: events)
			if (e.getStart().before(event.getStart()))
					idx ++;
		return idx;
	}
	
	public void setEventIndexByTime(List<Event> events){
		for (Event e: events)
			e.setIndex(getIndexOf(e, events));
	}
	
	/** An event handler method that search for the input query. */
    @DefaultHandler
    public Resolution search() {
    	QueryResponse qresponse = null;
    	
    	events = new ArrayList<Event>();
    	solrServer = Utils.getSolrServerForEvents();
        if (solrServer != null){
        	SolrQuery solrQuery = new SolrQuery();
        	solrQuery.setRows(NUM_ROWS);
        	solrQuery.setStart((pageEvent - 1) * NUM_ROWS);
        	solrQuery.setQuery(query);
        	//for highlighting
        	solrQuery.setHighlight(true);
        	solrQuery.setHighlightSimplePre("<strong>");
        	solrQuery.setHighlightSimplePost("</strong>");
        	solrQuery.setHighlightFragsize(50);
        	solrQuery.setHighlightSnippets(10);
        	
        	solrQuery.setFacet(true);
        	solrQuery.setFacetMinCount(1);
        	solrQuery.addFacetField("source", "entity");
        	solrQuery.setFacetLimit(2000);
        	
        	System.out.println("Query: " + solrQuery.getQuery());
        	        	
        	solrQuery.setFields("ID, start, end, entity, description");
        	
        	//apply filters
        	if (appliedFilters != null)
	        	for (String appliedFilter: appliedFilters.split("###"))
	        		if (appliedFilter.length() > 0)
	        			solrQuery.addFilterQuery(appliedFilter);        	
        	
        	//add date range
        	if (fromDate != null){
            	String fq = "start:[" + getFromDateStr() + "T00:00:00Z TO *]";
            	solrQuery.addFilterQuery(fq);        		
        	}
        	if (toDate != null)
        	{
            	String fq = "end:[* TO " + getToDateStr() + "T23:59:59Z]";
            	solrQuery.addFilterQuery(fq);
        	}
        	
        	System.out.println("Query: " + solrQuery.toString());
        	
        	//solrQuery.setHighlight(true);
        	//solrQuery.set("hl.simple.pre", "<h1>");
        	//solrQuery.set("hl.simple.post", "</h1>");
        	try {
				qresponse = solrServer.query(solrQuery);
			} catch (SolrServerException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        	
            if (qresponse != null){
            	events = qresponse.getBeans(Event.class);
            	
            	numFoundEvents = (int) qresponse.getResults().getNumFound();
            	
            	for (Event r: events){
            		if (qresponse.getHighlighting() != null)
            			r.setSnippets(qresponse.getHighlighting().get(r.getID()).get("text"));
            	}
            	
            	setEventIndexByTime(events);
            				
            	System.out.println("Event: " + events.size());        	
            	System.out.println("Num found: " + numFoundEvents);
            	
            	//get facets
            	filters = new ArrayList<>();
    			List<FacetField> queryFacets = new ArrayList<FacetField>();
    			queryFacets = qresponse.getFacetFields();    			
    			for (FacetField ff: queryFacets){
    				System.out.println(ff.getName());
    				System.out.println(ff.getValues());
    				Filter f = new Filter(ff.getName(), ff.getValues());
    				filters.add(f);
    			}
            	System.out.println("Num facets: " + filters.size());            	
            }        	
        }    	    	    	
        
        return new ForwardResolution("/results.jsp");
    }
    
    public String getLink(Event evt){
    	//System.out.println("Applied filters: " + appliedFilters);
    	String link = "<a href=\\\"Wiki.action?query=";
    	link += normalizeStr(query);
    	link += "&filterEvent=" + evt.getID();
    	link += "&addedFilter=" + normalizeStr(appliedFilters.replace("#", "%23"));
    	link += "&pageEvent=" + pageEvent;
    	if (fromDate != null) link += "&fromDate=" + fromDate;
    	if (toDate != null) link += "&toDate=" + toDate;
    	//link +="&appliedFilters=" + appliedFilters;
    	
    	link += "\\\"> Filter wiki pages entities in this event </a>";
    	//System.out.println(link);
    	return link;
    }

	public static String normalizeStr(String str){
		if (str == null)
			return "";
		str = str.replace("\\", "\\\\").replace("\"", "'");
		return str;
	}
	    
    public String getJsonOfEvent(Event evt){
		StringBuilder jsonForTimeline = new StringBuilder();
        jsonForTimeline.append("{\"startDate\":\"");
        Calendar cal = Calendar.getInstance();
        cal.setTime(evt.getStart());
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH)+1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        jsonForTimeline.append(year);
        jsonForTimeline.append(",");
        jsonForTimeline.append(month);
        jsonForTimeline.append(",");
        jsonForTimeline.append(day);
         jsonForTimeline.append("\",");
         
         cal.setTime(evt.getEnd());
         year = cal.get(Calendar.YEAR);
         month = cal.get(Calendar.MONTH)+1;
         day = cal.get(Calendar.DAY_OF_MONTH);
        jsonForTimeline.append("\"endDate\":\"");
        jsonForTimeline.append(year);
        jsonForTimeline.append(",");
        jsonForTimeline.append(month);
        jsonForTimeline.append(",");
        jsonForTimeline.append(day);
         jsonForTimeline.append("\",");
        jsonForTimeline.append("\"headline\":\"");   
        jsonForTimeline.append(normalizeStr(evt.getDescription()));
        jsonForTimeline.append("\",");
        jsonForTimeline.append("\"text\":\"");  
        jsonForTimeline.append("<p>");
        //jsonForTimeline.append(description.replaceAll("\\s+", " "));
        //jsonForTimeline.append(getLink(evt));
        jsonForTimeline.append("</p>");
        if (evt.getEntity() != null){
	         jsonForTimeline.append("<p>");
	         jsonForTimeline.append(normalizeStr(evt.getEntity().toString()));
	         jsonForTimeline.append("</p>");
        }
        
        jsonForTimeline.append("\",");
        jsonForTimeline.append("\"classname\":\"optionaluniqueclassnamecanbeaddedhere\",\"asset\":{}}");
        //System.out.println(jsonForTimeline.toString());
        return jsonForTimeline.toString();
    }
    
	public String getJsonForTimeline(){
		StringBuilder jsonForTimeline = new StringBuilder();
        jsonForTimeline.append("{\"timeline\":{\"headline\":\"");
        jsonForTimeline.append("Timeline for query: ");
        jsonForTimeline.append(normalizeStr(query));
        jsonForTimeline.append(" \",\"type\":\"default\",\"text\":\"<p>");
        jsonForTimeline.append(" Timeline for query " + normalizeStr(query) + " and the events " + ((pageEvent - 1) * NUM_ROWS + 1) + "->" + (pageEvent * NUM_ROWS));
        jsonForTimeline.append(" </p>\",\"asset\": {}, \"date\": [\n");
        
        for(Event evt:events){
        	try{
	        	//jsonForTimeline.append(evt.getJsonForTimeline());
	        	jsonForTimeline.append(getJsonOfEvent(evt));
	        	jsonForTimeline.append(",\n");
        	} catch (Exception e){
        		e.printStackTrace();
        	}
        }
        
        jsonForTimeline.append(" ]}};");

        
        System.out.println(jsonForTimeline.toString());
        return jsonForTimeline.toString();
	}
    
    public int getEventPagesNr(){
    	return (int) Math.floor((numFoundEvents - 1) / 10 + 1);
    }
    
    public int getWikiPagesNr(){
    	return (int) Math.floor((numFound - 1) / 10 + 1);
    }
    
    public static void main(String args[]){
    	Map<String, List<String>> m = new HashMap<String, List<String>>();
    	m.put("dang", new ArrayList<String>());
    	m.get("dang").add("duc");
    	System.out.println(m);
    	
    	Date d = new Date();
    	System.out.println(d);
    	System.out.println(new WikiActionBean().getDateStrWithYearOnly(d));
    	
    	int total = 0;
    	for (int i = 1; i <= 6; i++)
    		for (int j = 1; j <= 6; j++)
    			total += (i + j);
    	double e = total / 36.0;
    	    	
    	System.out.println(e);
    	
    	double start = 0, end = 100;
    	double lambda;
    	while (end - start > 0.0001){
    		lambda = (start + end) / 2;
    	}
    }
}
